package com.sensetime.stlivenesslibrary.ui;

import java.io.File;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.sensetime.stlivenesslibrary.LivenessDetector;
import com.sensetime.stlivenesslibrary.LivenessDetector.DetectStatus;
import com.sensetime.stlivenesslibrary.LivenessDetector.Motion;
import com.sensetime.stlivenesslibrary.LivenessDetector.Status;
import com.sensetime.stlivenesslibrary.STFinanceJNI;
import com.sensetime.stlivenesslibrary.STFinanceJNI.CVFinanceFrame;
import com.sensetime.stlivenesslibrary.util.Constants;
import com.sensetime.stlivenesslibrary.util.DataController;
import com.sensetime.stlivenesslibrary.util.LivenessUtils;

/**
 * 
 * @author Tracy
 * 
 *         实时人脸检测接口调用示例
 * 
 */
public class FaceOverlapFragment extends CameraOverlapFragment {
	private static final String TAG = "FaceOverlapFragment";
	private static final boolean DEBUG_PREVIEW = false;
	private OnLivenessCallBack mListener;
	private boolean mIsKilled = false;
	public boolean mPaused = true;
	private boolean mNV21DataIsReady = false;
	private byte mNv21[];
	private byte[] mTmp;
	private Motion [] mDetectList;
	private boolean mLiveResult[];
	private int mStatus = 0;
	public LivenessDetector mDetector = null;
	private long mStartTime;
	private int mFrameCount = 0;
	Bundle bundle = null;
	private Status status = null;
//	private float [] mThreshold;
//	private boolean [] mLost;
	private final static int mSystemRootStateUnknow = -1;
	private final static int mSystemRootStateDisable = 0;
	private final static int mSystemRootStateEnable = 1;
	private static int systemRootState = mSystemRootStateUnknow;
	private int livenessConfig;
	private int outputType;
	private int complexity;
	private boolean firstPreviewFrame = true;
	private long firstFrameTime;
	//TODO will changed to use the web return value
	private int detectWaitTime = 4 * 1000;
//	private int detectWaitTime = 1000 * SharedPreferencesManager.getInstance().getFaceCheckPreTime();
	private boolean beginShowWaitUIBoolean = true;
	private boolean endShowWaitUIBoolean = false;
	public boolean startAddSequentialInfo = false;
	private boolean lostTrue = false;
	private boolean detectorStartSuccess = false;
	public boolean createHandleSuccess = false;
	private ExecutorService executor;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {
		View view = super.onCreateView(inflater, container, savedInstanceState);
		bundle = getActivity().getIntent().getExtras();
		mDetectList = DataController.getMctionOrder(getActivity().getIntent()
				.getStringExtra(LivenessActivity.EXTRA_MOTION_SEQUENCE));
//		mLost = DataController.getLost(bundle.getString(Constants.LOST));
//		mThreshold = DataController.getThreshold(bundle
//				.getString(Constants.THRESHOLD));
//		if (mLost != null && mLost.length > 0) {
//			for (int i = 0; i < mLost.length; i++) {
//				if (mLost[i]) {
//					complexity = STFinanceJNI.WRAPPER_COMPLEXITY_EASY;
//					lostTrue = true;
//				}
//			}
//		}
//		if (!lostTrue) {
//			if (mThreshold != null && mThreshold.length > 0) {
//				if (mThreshold[0] >= 0.7f) {
//					complexity = STFinanceJNI.WRAPPER_COMPLEXITY_HELL;
//				} else if (mThreshold[0] < 0.7f && mThreshold[0] >= 0.5f) {
//					complexity = STFinanceJNI.WRAPPER_COMPLEXITY_HARD;
//				} else if (mThreshold[0] < 0.5f) {
//					complexity = STFinanceJNI.WRAPPER_COMPLEXITY_NORMAL;
//				}
//			}
//		}
		if (mDetectList.length > 0) {
			mLiveResult = new boolean[mDetectList.length];
			for (int i = 0; i < mDetectList.length; i++) {
				mLiveResult[i] = false;
			}
		}
		
		if (bundle.getString(LivenessActivity.COMPLEXITY).equalsIgnoreCase(Constants.EASY)) {
		    complexity = complexity = STFinanceJNI.WRAPPER_COMPLEXITY_EASY;
		} else if (bundle.getString(LivenessActivity.COMPLEXITY).equalsIgnoreCase(Constants.NORMAL)) {
		    complexity = complexity = STFinanceJNI.WRAPPER_COMPLEXITY_NORMAL;
		} else if (bundle.getString(LivenessActivity.COMPLEXITY).equalsIgnoreCase(Constants.HARD)) {
		    complexity = complexity = STFinanceJNI.WRAPPER_COMPLEXITY_HARD;
        } else if (bundle.getString(LivenessActivity.COMPLEXITY).equalsIgnoreCase(Constants.HELL)) {
            complexity = complexity = STFinanceJNI.WRAPPER_COMPLEXITY_HELL;
        }
		
		if (bundle.getString(Constants.OUTTYPE).equalsIgnoreCase(Constants.SINGLEIMG)) {
			outputType = STFinanceJNI.WRAPPER_OUTPUT_TYPE_SINGLE_IMAGE;
		} else if (bundle.getString(Constants.OUTTYPE)
				.equalsIgnoreCase(Constants.MULTIIMG)) {
			outputType = STFinanceJNI.WRAPPER_OUTPUT_TYPE_MULTI_IMAGE;
		} else if (bundle.getString(Constants.OUTTYPE)
				.equalsIgnoreCase(Constants.VIDEO)) {
			outputType = STFinanceJNI.WRAPPER_OUTPUT_TYPE_LOW_QUALITY_VIDEO;
		} else if (bundle.getString(Constants.OUTTYPE)
				.equalsIgnoreCase(Constants.FULLVIDEO)) {
			outputType = STFinanceJNI.WRAPPER_OUTPUT_TYPE_HIGH_QUALITY_VIDEO;
		}
		livenessConfig = outputType
				| STFinanceJNI.WRAPPER_LOG_LEVEL_ONLY_EXTERN | complexity;
		initStateAndPreviewCallBack();
		return view;
	}

	@Override
	public void onResume() {
		super.onResume();
		mIsKilled = false;
		if (executor == null) {
			executor = Executors.newSingleThreadExecutor();
		}
		executor.execute(new Runnable() {
			public void run() {
				while (!mIsKilled) {
					try {
						Thread.sleep(2);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					if (!mNV21DataIsReady || mPaused) {
						continue;
					}
					synchronized (mNv21) {
						if (mNv21 != null && mTmp != null
								&& mTmp.length >= mNv21.length) {
							System.arraycopy(mNv21, 0, mTmp, 0, mNv21.length);
						}
						mNV21DataIsReady = false;
					}
					if (!mPaused && endShowWaitUIBoolean) {
						synchronized (this) {
							startLivenessIfNeed();
						}
						trackFaces();
					}
				}
			}
		});
	}

	private void startAndSetStaticInfo() {
		if (mDetector != null) {
			createHandleSuccess = true;
			if (mDetector != null) {
				detectorStartSuccess = mDetector.start(livenessConfig);
				if (detectorStartSuccess) {
					setWrapperStaticInfo();
				}
			}
		}
	}

	private void trackFaces() {
		if (null == mDetector) {
			startLivenessIfNeed();
		}
		if(null != mDetector) {
			if(!detectorStartSuccess) {
				startAndSetStaticInfo();
			}
		}
		if (mDetector != null) {
			try {
				if(mStatus < mDetectList.length) {
					if(detectorStartSuccess) {
						status = mDetector.detect(mTmp, Constants.PREVIEW_WIDTH, Constants.PREVIEW_HEIGHT, mCameraInfo.orientation, mDetectList[mStatus]);
					}
				}
			} catch (Exception e) {
				 status.setDetectStatus(DetectStatus.INTERNAL_ERROR.getValue());
				e.printStackTrace();
			}
		}
		if (status == null) {
		    return ;
		}
		if (mStatus < mDetectList.length) {
			if (status.getDetectStatus() == LivenessDetector.DetectStatus.TRACKING_MISSED.getValue()) {
				if (mStatus > 0) {
					try {
						synchronized (this) {
							stopLiveness();
							mDetector.end();
						}
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					startAddSequentialInfo = false;
					LivenessActivity.saveEncryFile(getLivenessResult());
					mListener.onLivenessDetect(
							Constants.LIVENESS_TRACKING_MISSED, mStatus);
					mDetector.destroy();
					try {
						mDetector.finalize();
					} catch (Throwable e) {
						e.printStackTrace();
					}
					mDetector = null;
				} else {
					synchronized (this) {
						resetStatus(true);
						setConfigAndstartDetect();
					}
				}
			}
		}
		if (status.getDetectStatus() == LivenessDetector.DetectStatus.PASSED.getValue() && status.isPassed()) {
			if (mStatus < mDetectList.length) {
				mLiveResult[mStatus] = true;
				if (mLiveResult[mStatus]) {
					mStatus++;
					mDetector.detect(mTmp,
                            Constants.PREVIEW_WIDTH,
                            Constants.PREVIEW_HEIGHT,
                            mCameraInfo.orientation, Motion.NONE);
					if (mStatus == mDetectList.length) {
						mPaused = true;
						try {
							synchronized(this) {
								stopLiveness();
								mDetector.end();
							}
						} catch (Exception e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						startAddSequentialInfo = false;
						mListener.onLivenessDetect(Constants.LIVENESS_SUCCESS,
								mStatus);
					} else {
						restartDetect(true);
					}
				}
			}
		}
	}

	@Override
	public void onPause() {
		super.onPause();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		releaseLiveness();
		stopDetectThread();
	}

	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
	}

	public byte[] getLivenessResult() {
		try {
			return mDetector.getLivenessResult();
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	public CVFinanceFrame[] getImageResult() {
		try {
			return mDetector.getImageResult();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
	}

	/*
	 * Return whether the system is root or not.
	 */
	public static boolean isRootSystem() {
		systemRootState = mSystemRootStateDisable;
		File f = null;
		final String kSuSearchPaths[] = { "/system/bin/", "/system/xbin/",
				"/system/sbin/", "/sbin/", "/vendor/bin/" };
		try {
			for (int i = 0; i < kSuSearchPaths.length; i++) {
				f = new File(kSuSearchPaths[i] + "su");
				if (f != null && f.exists()) {
					systemRootState = mSystemRootStateEnable;
				}
			}
		} catch (Exception e) {
		}
		if(systemRootState == mSystemRootStateEnable){
			return true;
		} else {
			return false;
		}
	}
	
	/*
	 * set the WrapperStaticInfo here.
	 */
	public void setWrapperStaticInfo() {
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.DEVICE.getValue(), android.os.Build.MODEL);
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.OS.getValue(), "Android");
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.SDK_VERSION.getValue(),
							LivenessDetector.getSDKVersion());
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.SYS_VERSION.getValue(),
							android.os.Build.VERSION.RELEASE);
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.ROOT.getValue(), String.valueOf(isRootSystem()));
		} catch (Exception e) {
			e.printStackTrace();
		}
		try {
			mDetector.setStaticInfo(LivenessDetector.WrapperStaticInfo.CUSTOMER.getValue(),  getActivity().getApplicationContext().getPackageName());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void initStateAndPreviewCallBack() {
		mStatus = 0;
		mNv21 = new byte[Constants.PREVIEW_WIDTH * Constants.PREVIEW_HEIGHT * 3
				/ 2];
		mTmp = new byte[Constants.PREVIEW_WIDTH * Constants.PREVIEW_HEIGHT * 3
				/ 2];
		this.setPreviewCallback(new PreviewCallback() {
			@Override
			public void onPreviewFrame(byte[] data, Camera camera) {
				if (DEBUG_PREVIEW) {
					debugFps();
				}
				if (firstPreviewFrame) {
					firstFrameTime = System.currentTimeMillis();
					firstPreviewFrame = false;
				}
				long intervalTime = System.currentTimeMillis() - firstFrameTime;
				if (intervalTime < detectWaitTime) {
					if (beginShowWaitUIBoolean) {
						mListener.onLivenessDetect(Constants.DETECT_BEGIN_WAIT,
								1);
						beginShowWaitUIBoolean = false;
					}
				} else {
					if (!endShowWaitUIBoolean) {
						mListener
								.onLivenessDetect(Constants.DETECT_END_WAIT, 1);
						endShowWaitUIBoolean = true;
						startLiveness();
					}
					if (!mPaused) {
						synchronized (mNv21) {
							if (data != null && mNv21 != null
									&& mNv21.length >= data.length) {
								System.arraycopy(data, 0, mNv21, 0, data.length);
								mNV21DataIsReady = true;
							}
						}
					}
				}
			}
		});
	}

	public void startLivenessIfNeed() {
		if (mDetector == null) {
			try {
				mDetector = new LivenessDetector(getActivity());
				if(mDetector.createHandle()) {
					createHandleSuccess = true;
					if (mDetector != null) {
						detectorStartSuccess = mDetector.start(livenessConfig);
						if (detectorStartSuccess) {
							setWrapperStaticInfo();
						}
					}
				} else {
					onErrorHappen(LivenessActivity.RESULT_CREATE_HANDLE_ERROR);
				}
			} catch (Throwable e) {
				onErrorHappen(LivenessActivity.RESULT_CREATE_HANDLE_ERROR);
			}
		}
	}

	private void releaseLiveness() {
		if (mDetector != null) {
			mDetector = null;
		}
	}

	private void stopDetectThread() {
		mIsKilled = true;
		createHandleSuccess = false;
		executor.shutdown();
		try {
			executor.awaitTermination(100, TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		executor = null;
	}

	void restartDetect(boolean bRestartTime) {
		if (bRestartTime) {
			mListener.onLivenessDetect(mDetectList[mStatus].getValue(), mStatus);
		}
	}

	public void setConfigAndstartDetect() {
		if (null != mDetector) {
			mDetector.end();
			boolean saveFileSuccess = LivenessUtils.saveFile(getLivenessResult(), LivenessActivity.EXTRA_RESULT_PATH + LivenessActivity.LIVENESS_FILE_NAME);
			if(!saveFileSuccess) {
				onErrorHappen(getActivity().RESULT_FIRST_USER);
			}
			mDetector.destroy();
			try {
				mDetector.finalize();
			} catch (Throwable e) {
				e.printStackTrace();
			}
			mDetector = null;
		}
	}

	public void resetStatus(boolean fAlert) {
		boolean bRestartTime = fAlert;
		if (mStatus > 0) {
			bRestartTime = true;
		}
		resetLivenessResult();
		mStatus = 0;
		restartDetect(bRestartTime);
	}

	private void resetLivenessResult() {
		int count = mLiveResult.length;
		for (int i = 0; i < count; i++) {
			mLiveResult[i] = false;
		}
	}

	public void registerLivenessDetectCallback(OnLivenessCallBack callback) {
		mListener = callback;
	}

	public interface OnLivenessCallBack {
		public void onLivenessDetect(int value, int status);
	}

	public void stopLiveness() {
		mPaused = true;
	}

	public void startLiveness() {
		mPaused = false;
	}

	private void debugFps() {
		if (mFrameCount == 0) {
			mStartTime = System.currentTimeMillis();
		}
		mFrameCount++;
		long testTime = System.currentTimeMillis() - mStartTime;
		if (testTime > 1000) {
			Log.i(TAG, "onPreviewFrame FPS = " + mFrameCount);
			Toast.makeText(getActivity(), "FPS: " + mFrameCount, Toast.LENGTH_SHORT).show();
			mFrameCount = 0;
		}
	}

}
